package c2

import (
	"errors"
	"fmt"
	"log"
	"os"
	"reflect"
	"strconv"
	"strings"
	"time"

	"gitee.com/tuboyou/c2/check"
	"gitee.com/tuboyou/c2/request"
	"gitee.com/tuboyou/c2/u2"
)

type Response request.Response

// GetExpectBodyOnce 获取第一个期望的响应体结构
//
// 返回值：
//
//	interface{} - 第一个期望结构的值
//
// 注意：
//
//	如果未设置任何期望结构，返回nil
func (t Response) GetExpectBodyOnce() interface{} {
	x := request.Response(t).GetExpectBody()
	if x == nil {
		log.Println("expectBody not found")
		return nil
	}
	if len(x) == 0 {
		log.Println("expectBody not found")
		return nil
	}
	for _, v := range x {
		return v
	}
	return nil
}

// GetExpectBody 获取期望的响应体结构
//
// 参数：
//
//	name - 期望结构的名称
//
// 返回值：
//
//	interface{} - 期望结构的值
//
// 注意：
//
//	如果未找到对应名称的期望结构，返回nil
func (t Response) GetExpectBody(name string) interface{} {
	x := request.Response(t).GetExpectBody()
	if x == nil {
		log.Println("expectBody not found")
		return nil
	}
	if v, ok := x[name]; !ok {
		log.Println("expectBody not found")
		return nil
	} else {
		return v
	}
}

// String 获取响应结果的字符串表示
//
// 返回值：
//
//	string - 响应结果的JSON格式字符串
func (r Response) String() string {
	return request.Response(r).String()
}

// Bind 将响应结果绑定到结构体
//
// 参数：
//
//	struct_data - 要绑定的结构体指针
//
// 返回值：
//
//	error - 绑定过程中发生的错误
//
// 注意：
//
//  1. struct_data必须是指针类型
//  2. 如果struct_data不是指针，会自动转换为指针
func (r Response) Bind(struct_data interface{}) error {
	if reflect.TypeOf(struct_data).Kind() != reflect.Ptr {
		struct_data = &struct_data
		log.Println("[error]body is not ptr")
	}
	return u2.Json2Interface([]byte(r.Result), struct_data)
}

// Check 校验响应结果
//
// 参数：
//
//	code - 期望的HTTP状态码
//	header - 期望的响应头
//	body - 期望的响应体
//
// 返回值：
//
//	string - 校验结果，"ok"表示校验通过，否则返回错误信息
func (r Response) Check(code int, header map[string]string, body interface{}) string {
	errs := r.checkResult(code, header, body)
	if len(errs) == 0 {
		return "ok"
	}
	sss := "errs:\n"
	for i, v := range errs {
		sss += strconv.Itoa(i) + " " + v.Error() + "\n"
	}
	return sss
}

// GetResult 获取响应结果
//
// 返回值：
//
//	interface{} - 响应结果
func (r Response) GetResult() interface{} {
	return request.Response(r).GetResult()
}

// checkResult 校验响应结果
//
// 参数：
//
//	code - 期望的HTTP状态码
//	header - 期望的响应头
//	body - 期望的响应体
//
// 返回值：
//
//	[]error - 校验过程中发现的错误列表
//
// 注意：
//
//  1. 会校验状态码、响应头和响应体
//  2. 如果响应体为空，会返回错误
//  3. 如果JSON解析失败，会返回错误
func (r Response) checkResult(code int, header map[string]string, body interface{}) []error {
	errList := make([]error, 0)
	//判断内置错误
	if r.Error != nil {
		return append(errList, r.Error)
	}
	//判断响应码
	if r.StatusCode != code {
		return append(errList, errors.New("StatusCodeError:"+strconv.Itoa(r.StatusCode)))
	}
	//判断header
	rheader := request.Response(r).Headers
	if rheader == nil {
		rheader = make(map[string]string)
	}
	for k, v := range header {
		v2, ok := rheader[k]
		if !ok {
			errList = append(errList, errors.New("HeaderError:"+k+" not found"))
			continue
		}
		if v2 != v {
			errList = append(errList, errors.New("HeaderError:"+k+" value not match, expect:"+v+" actual:"+v2))
		}
	}
	//不校验结果
	if body == nil {
		return errList
	}
	//没有响应数据直接返回
	if r.Result == "" {
		errList = append(errList, errors.New("BodyError:empty"))
		return errList
	}
	// map序列化
	var m map[string]interface{}
	err := u2.Json2Interface([]byte(r.Result), &m)
	if err != nil {
		errList = append(errList, errors.New("BodyMapError:"+err.Error()))
		return errList
	}
	//json序列化
	err = r.Bind(body)
	if err != nil {
		errList = append(errList, errors.New("BodyJsonError:"+err.Error()))
		return errList
	}
	//比较序列化数据
	return check.StructCheck(body, m, errList)
}

// PrintStruct 打印结构体含期望结果，不含请求信息
//
// 注意：
//
//  1. 如果响应为空，会打印空响应信息
//  2. 如果JSON解析失败，会打印错误信息
//  3. 会打印响应耗时
func (r Response) PrintStruct() {
	if r.Result == "" {
		fmt.Println("response is empty")
		fmt.Println("status_code:", r.StatusCode, "cost", r.Cost, "ms")
		if r.Error != nil {
			fmt.Println("error:", r.Error)
		}
		return
	}
	var f interface{}
	err := u2.Json2Interface([]byte(r.Result), &f)
	if err != nil {
		fmt.Println("------------------------")
		fmt.Println("转 json 格式报错：\n", err)
		fmt.Println("------------------------")
		fmt.Println(r.StatusCode, r.Reason, "\n", r.Result, "\n", r.Cost, "ms")
		return
	}
	fmt.Println(printJson(f, "", "\n%s", true, true, true))
	fmt.Println("cost", r.Cost, "ms")

}

// PrintStructNoResult 打印结构体，不含请求信息和期望结果
//
// 注意：
//
//  1. 如果响应为空，会打印空响应信息
//  2. 如果JSON解析失败，会打印错误信息
//  3. 会打印响应耗时
func (r Response) PrintStructNoResult() {
	if r.Result == "" {
		fmt.Println("response is empty")
		fmt.Println("status_code:", r.StatusCode, "cost", r.Cost, "ms")
		if r.Error != nil {
			fmt.Println("error:", r.Error)
		}
		return
	}
	var f interface{}
	err := u2.Json2Interface([]byte(r.Result), &f)
	if err != nil {
		fmt.Println("------------------------")
		fmt.Println("转 json 格式报错：\n", err)
		fmt.Println("------------------------")
		fmt.Println(r.StatusCode, r.Reason, "\n", r.Result, "\n", r.Cost, "ms")
		return
	}
	fmt.Println(printJson(f, "", "\n%s", true, true, false))
	fmt.Println("cost", r.Cost, "ms")

}

// PrintRequestInfo 打印请求数据
//
// 参数：
//
//	showHeader - 是否显示请求头信息
//
// 注意：
//
//  1. 如果请求方法是POST，会自动显示请求头
//  2. 如果请求体不为空，会打印请求体内容
func (r Response) PrintRequestInfo(showHeader bool) {
	fmt.Println("Method:", r.Req.Method)
	fmt.Println(r.Req.Url)
	fmt.Println()

	if showHeader || r.Req.Method == "POST" {
		fmt.Println("Headers:")
		for k, v := range r.Req.Headers {
			fmt.Println(k, ":", v)
		}
	}
	if r.Req.Body != "" {
		fmt.Println("Body:")
		fmt.Println(r.Req.Body)
	}
}

// PrintResponseInfo 打印响应数据
//
// 参数：
//
//	showStatusCode - 是否显示状态码
//	showHeader - 是否显示响应头
//
// 注意：
//
//  1. 如果状态码大于399，会打印错误信息
//  2. 如果JSON解析失败，会打印原始响应内容
func (r Response) PrintResponseInfo(showStatusCode, showHeader bool) {
	if showStatusCode {
		fmt.Println(r.StatusCode, r.Reason)
	}
	if showHeader {
		for k, v := range request.Response(r).Headers {
			fmt.Println(k, ":", v)
		}
	}
	if r.Result == "" {
		fmt.Println("response is empty")
		fmt.Println("status_code:", r.StatusCode, "cost", r.Cost, "ms")
		if r.Error != nil {
			fmt.Println("error:", r.Error)
		}
		return
	}
	if r.StatusCode > 399 {
		fmt.Println("status_code:", r.StatusCode, "cost", r.Cost, "ms")
		fmt.Println("\n" + r.Result)
		return
	}
	var f interface{}
	err := u2.Json2Interface([]byte(r.Result), &f)
	if err != nil {
		fmt.Println("------------------------")
		fmt.Println(err)
		fmt.Println("------------------------")
		fmt.Println(r.Result)
		return
	}
	printJson(f, "", "\n%s", false, true, false)

	fmt.Println("\ncost", r.Cost, "ms")
}

// PrintStructInfo 打印结构体，含请求信息
//
// 注意：
//
//  1. 会调用PrintRequestInfo和PrintStruct方法
//  2. 不显示请求头信息
func (r Response) PrintStructInfo() {
	r.PrintRequestInfo(false)
	r.PrintStruct()
}

// PrintInfo 打印请求和响应基本数据
//
// 注意：
//
//  1. 会调用PrintRequestInfo和PrintResponseInfo方法
//  2. 不显示请求头和响应头信息
func (r Response) PrintInfo() {
	r.PrintRequestInfo(false)
	r.PrintResponseInfo(false, false)
}

// PrintDetail 打印请求和响应详细信息
//
// 注意：
//
//  1. 会调用PrintRequestInfo和PrintResponseInfo方法
//  2. 显示请求头和响应头信息
func (r Response) PrintDetail() {
	r.PrintRequestInfo(true)
	fmt.Println()
	r.PrintResponseInfo(true, true)
}

// Curl 打印curl命令
//
// 注意：
//
//  1. 会生成包含请求方法、URL、请求体和请求头的curl命令
func (r Response) Curl() {
	fmt.Println(r.curlString())
}

// CurlString 返回curl命令
//
// 返回值：
//
//	string - 生成的curl命令
//
// 注意：
//
//  1. 会生成包含请求方法、URL、请求体和请求头的curl命令
func (r Response) curlString() string {
	command := fmt.Sprintf("curl -X %s '%s'", r.Req.Method, r.Req.Url.String())

	if r.Req.Body != "" {
		command += fmt.Sprintf(" -d '%s'", r.Req.Body)
	}

	for key, value := range r.Req.Headers {
		command += fmt.Sprintf(" -H '%s: %s'", key, value)
	}
	return command
}

// Curl2File 写入curl命令到文件
//
// 参数：
//
//	p - 文件路径
//
// 注意：
//
//  1. 如果文件已存在，会覆盖原有内容
//  2. 文件权限设置为0666
func (r Response) Curl2File(p string) {
	os.WriteFile(p, []byte(r.curlString()), 0666)
}

// Result2File 结果写入文件,需要包含文件名
//
// 参数：
//
//	p - 文件路径
//	hasRequest - 是否写入请求信息
//	hasHeader - 是否写入响应头信息
//
// 注意：
//
//  1. 如果路径以/结尾，会自动生成文件名
//  2. 如果响应为空，会写入空响应信息
//  3. 如果发生错误，会写入错误信息
func (r Response) Result2File(p string, hasRequest, hasHeader bool) {
	if p == "" {
		// fmt.Println("result path is empty")
		return
	}
	if strings.HasSuffix(p, "/") {
		p = p + fmt.Sprintf("ResultFile_%d.txt", time.Now().Unix())
	}
	content := ""
	if hasRequest {
		content += r.Req.Url.String()
	}
	if hasHeader {
		content += "\n"
		for k, v := range request.Response(r).Req.Headers {
			content += fmt.Sprintf("  '%s: %s' \n", k, v)
		}
	}
	if r.Req.Body != "" {
		content += "    body->" + r.Req.Body + "\n"
	} else {
		content += "\n"
	}

	if hasHeader {
		content += "\n"
		content += fmt.Sprintf("status_code:%d,reason:%s\n", r.StatusCode, r.Reason)
		for k, v := range r.Headers {
			content += fmt.Sprintf("  '%s: %s'\n", k, v)
		}
	}

	res := r.Result
	if len(res) < 2 {
		res = fmt.Sprintf("status_code:%d,No Result", r.StatusCode)
	}
	content += res
	if r.Error != nil {
		content += fmt.Sprintf("status_code:%d,Error:%s", r.StatusCode, r.Error.Error())
	}
	if strings.HasSuffix(res, "\n") {
		u2.AppendFile(p, content)
	} else {
		u2.AppendFile(p, content+"\n")
	}
}
